package com.zenika.zentracker.spring;

import java.lang.reflect.Field;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;

import com.zenika.zentracker.hibernate.ZenTrackerSessionFactory;

public class ZenTrackerLocalSessionFactoryBean extends LocalSessionFactoryBean implements ApplicationContextAware {

    private static final Log log = LogFactory.getLog("com.zenika.ZenTracker");

    private ApplicationContext applicationContext;

    protected SessionFactory newSessionFactory(Configuration config) throws HibernateException {
        SessionFactory sessionFactory = super.newSessionFactory(config);

        try {
            updateCurrentSessionContext(sessionFactory);
        } catch (Exception e) {
            log.warn("It was impossible to modify the current context with the Spring Application Context. "
                    + "You will be missing some features like the JMX export", e);
        }

        return new ZenTrackerSessionFactory(sessionFactory, applicationContext);
    }

    private void updateCurrentSessionContext(SessionFactory sessionFactory) throws Exception {
        Class ownerClass = Class.forName(sessionFactory.getClass().getName());
        Field f[] = ownerClass.getDeclaredFields();
        for (int i = 0; i < f.length; i++) {
            Field field = f[i];
            field.setAccessible(true);
            Object fieldValue = field.get(sessionFactory);
            if ((fieldValue != null) &&
                (ZenTrackerSpringSessionContext.class.isAssignableFrom(fieldValue.getClass()))) {
                    ((ZenTrackerSpringSessionContext) fieldValue).setApplicationContext(applicationContext);
                    break;
            }
            field.setAccessible(false);
        }
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
